<?php

namespace app\admin\service;

use app\common\cache\ExportCache;
use tmcore\base\BaseService;
use tmcore\services\ExportExcelService;
use app\common\enum\ExportEnum;
use app\common\model\log\ExportFileLog;
use tmcore\utils\Helper;
use ReflectionClass;

class ExportService extends BaseService
{
    /**
     * excel 导出
     * @param array $data 数据（支持分页）
     * @param string $file_name 文件名称
     * @param array $header 导出表头
     * @param array $action_info 说明内容 ['name'=>'操作者','time'=>'','mobile'=> '']
     * @param array $async_fun 异步调用方法 ['class_name'=>'类','method_name'=>'方法']
     * @return array|boolean
     */
    public static function exportExcel($data, $file_name, $header, $action_info = [], $async_fun = []) {
        // 判断是否为导出分页
        if(isset($data['extend']['operation_type']) && $data['extend']['operation_type'] == 'TM_EXCEL_EXPORT' ) {
            $export_params = $data['extend'];
            if($export_params['export_type'] == 1) { // 导出预览
                $data['export_title'] = $file_name;
                $data['export_suffix'] = 'xlsx';
                $data['page_sum'] = ceil($data['page_total'] / $data['page_limit']);
                $data['max_number'] = ExportEnum::MAX_COUNT;
                unset($data['list'],$data['extend']);
                return $data;
            } else if($export_params['export_type'] == 2) { // 导出或加入异步队列
                // 判断是否异步
                if(!$export_params['is_async']) {
                    return self::executeExportExcel($data['list'], $file_name, $export_params['file_suffix'], $header, $action_info);
                } else {
                    // 异步分页，调用任务队列
                    $export_class_name = $async_fun['class_name']??((debug_backtrace()[1]['class'])??'');
                    // 检验调用类和方法
                    if (!class_exists($export_class_name)) return self::setError($export_class_name .' 类不存在');
                    $method_name = $async_fun['method_name']??((debug_backtrace()[1]['function'])??'export');
                    $reflectionClass = new ReflectionClass($export_class_name);
                    if(!$reflectionClass->hasMethod($method_name)) return self::setError($method_name . ' 方法不存在');

                    // 导出job
                    $job_class_name = 'app\common\jobs\ExportJob';
                    $job_method_name = 'pushJob';

                    try {
                        // 文件标识
                        $identifier = Helper::get_random(32,'0123456789');
                        $file_random = self::getFileRandom();

                        $job_data = [
                            'job' => 'exportJob',
                            'data' => [
                                'file_identifier' => $identifier,
                                'file_name' => $file_name,
                                'file_suffix' => $export_params['file_suffix'],
                                'file_random' => $file_random,
                                'header' => $header,
                                'action_info' => $action_info,
                                'export_class_name' => $export_class_name,
                                'export_method_name' => $method_name,
                                'params' => $export_params['params']
                            ]
                        ];
                        // 调用添加队列静态方法
                        $job_id = call_user_func([$job_class_name, $job_method_name],$job_data);

                        // 添加导出日志
                        $params = [
                            'type' => 2,
                            'file_identifier' => $identifier,
                            'file_name' => $file_name,
                            'file_suffix' => $export_params['file_suffix'],
                            'admin_id' => $action_info['id']??0,
                            'param' => $job_data,
                            'job_id' => $job_id,
                            'job' => $job_class_name,
                            'export_job' => $export_class_name . '\\' . $method_name
                        ];
                        self::addExportFileLog($params);

                        (new ExportCache)->setExportFile(
                            $identifier,
                            [
                                'name' => $params['file_name'] . '_' . $file_random . '.' . $params['file_suffix'],
                                'job_id' => $job_id
                            ]
                        );

                        return ['file_identifier' => $identifier];
                    } catch (\Exception $e) {
                        return self::setError($e->getMessage());
                    }
                }
            } else if($export_params['export_type'] == 3){ // 异步导出
                return self::asyncExecuteExportExcel($export_params['file_identifier'], $data['list'], $file_name, $export_params['file_suffix'], $export_params['file_random'] ?? '', $header, $action_info);
            } else {
                return self::setError('导出参数异常');
            }
        } else {
            if(strpos('.', $file_name) !== false) {
                $parts = explode('.', $file_name);
                $file_name = $parts[0];
                $file_suffix = $parts[1];
            } else {
                $file_suffix = 'xlsx';
            }

            // 兼容分页数据
            if(isset($data['page_num']) && isset($data['page_limit']) && isset($data['list'])) {
                $data = $data['list'];
            }
            //普通导出
            return self::executeExportExcel($data, $file_name, $file_suffix,$header, $action_info);
        }
    }


    /**
     * 执行导出操作
     * @param $data
     * @param $file_name
     * @param $file_suffix
     * @param $header
     * @param array $action_info
     * @return array
     */
    public static function executeExportExcel($data, $file_name, $file_suffix, $header, $action_info = []) {
        $instance = ExportExcelService::instance();
        $identifier = $instance->getIdentifier();
        $file_identifier = Helper::get_random(32,'0123456789');
        // 添加导出日志
        $params = [
            'type' => 1,
            'file_identifier' => $file_identifier,
            'file_name' => $file_name,
            'file_suffix' => $file_suffix,
            'admin_id' => $action_info['id']??0,
        ];
        self::addExportFileLog($params);
        $start = microtime(true);
        $url = $instance
            ->setHeader($header)
            ->setTitle($file_name,$action_info)
            ->setData($data)
            ->exportExcel($file_name,$file_suffix);
        $file_name = $instance->getFileName();

        (new ExportCache)->setExportDownload($identifier, $url, $file_name);

        // 更新导出日志
        ExportService::updateExportFileLog(strval($file_identifier),[
            'file_url' => $url,
            'export_progress' => 100,
            'count' => count($data),
            'size' => round(filesize(app()->getRootPath() . $url) / 1024,2),
            'execute_time' => number_format((microtime(true)-$start), 4),
            'result' => 1,
            'result_msg' => ''
        ]);
        return ['url' => $url, 'file_key' => $identifier, 'file_name' => $file_name];
    }

    /**
     * 异步执行导出操作
     * @param $file_identifier
     * @param $data
     * @param $file_name
     * @param $file_suffix
     * @param $file_random
     * @param $header
     * @param array $action_info
     * @return array
     */
    public static function asyncExecuteExportExcel($file_identifier, $data, $file_name, $file_suffix, $file_random, $header, $action_info = []) {
        // 去除内存限制
        ini_set ('memory_limit','-1');
        $instance = ExportExcelService::instance();
        $identifier = $instance->getIdentifier();
        $exportCache = (new ExportCache);
        $exportCache->setExportFile($file_identifier, ['step' => 3]);
        $url = $instance
            ->setFileSuffix($file_random)
            ->setHeader($header)
            ->setTitle($file_name, $action_info)
            ->setData($data,function ($progress, $count, $total) use ($file_identifier, $exportCache) {
                // 更新写入数据进度
                $exportCache->setExportFile(
                    $file_identifier,
                    [
                        'progress' => $progress
                    ]
                );
            })
            ->exportExcel($file_name, $file_suffix);
        // 更新下载缓存
        $exportCache->setExportDownload($identifier, $url, ($file_name . '_' . $file_random . '.' . $file_suffix));
        // 导出完成，更新导出缓存
        $exportCache->setExportFile(
            $file_identifier,
            [
                'step' => 4,
                'progress' => 100,
                'url' => $url,
                'file_key' => $identifier
            ]
        );
        // 更新导出日志
        ExportService::updateExportFileLog(strval($file_identifier),[
            'file_url' => $url,
            'export_progress' => 100,
            'count' => count($data),
            'size' => round(filesize(app()->getRootPath() . $url) / 1024,2),
            'execute_time' => number_format((microtime(true)-($exportCache->getExportFile($file_identifier)['create_time'])), 4),
            'result' => 1,
            'result_msg' => ''
        ]);
        return ['url' => $url, 'file_key' => $identifier, 'file_name' => $file_name];
    }

    /**
     * 获取文件随机数
     * @return string
     */
    public static function getFileRandom()
    {
        return date('YmdHis') . '_' . Helper::get_random(6, '0123456789');
    }

    /**
     * 添加导出文件记录
     * @param array $params
     */
    public static function addExportFileLog($params) {

        $data = [
            'file_identifier' => $params['file_identifier'],
            'file_name' => $params['file_name'],
            'file_suffix' => $params['file_suffix'],
            'export_progress' => 0,
            'type' => $params['type']?? 1,
            'admin_id' => $params['admin_id']?? 0,
            'param' => !empty($params['param'])?json_encode($params['param'], 320):'',
            'job_id' => $params['job_id']?? '',
            'job' => $params['job'] ?? '',
            'export_job' => $params['export_job'] ?? '',
            'size' => '0',
            'count' => 0
        ];
        ExportFileLog::create($data);
    }


    /**
     * 编辑导出文件记录
     * @param string|int $id id|job_id|file_identifier
     * @param array $params
     */
    public static function updateExportFileLog($id, $params) {

        $data = [];
        if(isset($params['file_url'])) $data['file_url'] = $params['file_url'];
        if(isset($params['export_progress'])) $data['export_progress'] = $params['export_progress'];
        if(isset($params['count'])) $data['count'] = $params['count'];
        if(isset($params['size'])) $data['size'] = $params['size'];
        if(isset($params['execute_time'])) $data['execute_time'] = $params['execute_time'];
        if(isset($params['result'])) $data['result'] = $params['result'];
        if(isset($params['result_msg'])) $data['result_msg'] = $params['result_msg'];

        $where = [];
        if(preg_match('/[a-zA-Z]/',$id)) {
            $where['job_id'] = $id;
        } else if(preg_match('/^\d{32}$/',$id)) {
            $where['file_identifier'] = $id;
        } else {
            $where['id'] = $id;
        }

        if($data)ExportFileLog::where($where)->update($data);
    }
}